﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using AbstractBugTracker;
using AbstractProducts;
using log4net;
using SmartAssembly;

namespace Engine
{
    public sealed class SyncEngine<TBug, TBugDef> : IDisposable
        where TBug : class, IBug
        where TBugDef : BugDef
    {
        private static readonly ILog s_Log = LogManager.GetLogger("Engine.SyncEngine");

        private readonly IBugTracker<TBug, TBugDef> m_BugTracker;
        private readonly IReportRepository m_ReportRepository;
        private readonly HashSet<string> m_InternalSubmitterIpsAndGuids;
        private readonly ReportProcessingPipeline<TBug, TBugDef> m_ReportProcessingPipeline;

        public SyncEngine(IBugPresenter<TBug, TBugDef> bugPresenter, 
                          IBugTracker<TBug, TBugDef> bugTracker, 
                          IReportRepository reportRepository, 
                          IEmailSender emailSender, 
                          IDictionary<string, string> emailTemplates, 
                          HashSet<string> internalSubmitterIpsAndGuids,
                          IEnumerable<IPluginComponent<TBug, TBugDef>> plugins)
        {
            m_BugTracker = bugTracker;
            m_ReportRepository = reportRepository;
            m_InternalSubmitterIpsAndGuids = internalSubmitterIpsAndGuids;
            m_ReportProcessingPipeline = new ReportProcessingPipeline<TBug, TBugDef>(bugPresenter, bugTracker, emailSender, emailTemplates, plugins);
        }

        public void Dispose()
        {
            m_ReportProcessingPipeline.Dispose();
        }

        public void SyncReportsToBugTracker()
        {
            IEnumerable<ProductRef> linkedProducts = m_BugTracker.LinkedProducts;

            s_Log.InfoFormat("Found {0} linked products", linkedProducts.Count());

            foreach (ProductRef eachProduct in linkedProducts)
            {
                DateTime earliestBuildDate = m_BugTracker.GetEarliestBuildDate(eachProduct);

                s_Log.InfoFormat("Getting unprocessed reports for {0} since {1}", eachProduct, earliestBuildDate);

                foreach (Report report in m_ReportRepository.GetUnprocessedReports(eachProduct, earliestBuildDate))
                {
                    string key = String.Empty;
                    try
                    {
                        key = m_ReportProcessingPipeline.ProcessReport(report);
                    }
                    catch (WebException we)
                    {
                        s_Log.Warn("WebException:", we);
                        s_Log.Warn(new StreamReader(we.Response.GetResponseStream()).ReadToEnd());
                    }
                    catch (Exception e)
                    {
                        s_Log.Warn(String.Format("Exception processing report with id {0}. Marking as processed to limit damage. If you want this report, manually mark it as unprocessed.", report.Id), e);
                    }

                    m_ReportRepository.MarkAsProcessed(report, key, IsInternalReport(report));
                }
            }
        }

        private bool IsInternalReport(Report report)
        {
            string submitterIpOrGuid = report.SubmitterIpOrGuid;
            return submitterIpOrGuid != null && m_InternalSubmitterIpsAndGuids.Contains(submitterIpOrGuid);
        }
    }
}
